﻿#region Usings

using System;
using System.Collections.Generic;
using System.Xml.XPath;
using Yandex.Market.Api.YmlSerialization;

#endregion

namespace Yandex.Market.Api.Model
{
    /// <summary>
    ///   В YML существуют несколько типов описаний предложений товаров: "упрощенный", "vendor.model", "book", "audiobook", "artist.title", "tour" и "event-ticket".<br /> 
    ///   В зависимости от типа описания используются соответствующие поля, согласно прилагаемой схеме DTD (см. приложение).<br />  
    ///   Товарные предложения, описанные не в соответствии со своим типом, могут не приниматься к публикации.<br />
    /// </summary>
    /// <remarks>
    ///   Elements order: start OfferBase 0-190 -> Derived classes 200-390 -> end OfferBase 400-...
    /// </remarks>
    [YmlSerializeAs("offer")]
    [Serializable]
    public abstract class OfferBase : IOffer
    {
        protected OfferBase()
        {
            CategoryIds = new List<CategoryID>();
        }

        protected OfferBase(IOffer offer)
        {
            ID = offer.ID;
            Available = offer.Available;
            BID = offer.BID;
            CBID = offer.CBID;
            Url = offer.Url;
            BuyUrl = offer.BuyUrl;
            Price = offer.Price;
            Wprice = offer.Wprice;
            CurrencyId = offer.CurrencyId;
            Xcategory = offer.Xcategory;
            CategoryIds = offer.CategoryIds;
            Picture = offer.Picture;
            Store = offer.Store;
            Pickup = offer.Pickup;
            Delivery = offer.Delivery;
            DeliveryIncluded = offer.DeliveryIncluded;
            LocalDeliveryCost = offer.LocalDeliveryCost;
            OrderingTime = offer.OrderingTime;
            Aliases = offer.Aliases;
            Additionals = offer.Additionals;
            Description = offer.Description;
            SalesNotes = offer.SalesNotes;
            Promo = offer.Promo;
            ManufacturerWarranty = offer.ManufacturerWarranty;
            CountryOfOrigin = offer.CountryOfOrigin;
            Downloadable = offer.Downloadable;
            Adult = offer.Adult;
            Barcodes = offer.Barcodes;
            Params = offer.Params;
        }

        #region IOffer Members

        /// <summary>
        ///   ID товара. Обязательный элемент.
        /// </summary>
        [YmlAttributeForClass]
        [YmlSerializeAs("id")]
        [YmlAttributeOrder(0)]
        public uint ID { get; set; }

        ///<summary>
        ///  Статус доступности товара - в наличии/на заказ<br />
        ///  available="false" - товарное предложение на заказ. <br />
        ///  Магазин готов осуществить поставку товара на указанных условиях в течение месяца <br />
        ///  (срок может быть больше для товаров, которые всеми участниками рынка поставляются только на заказ). <br />
        ///  Те товарные предложения, на которые заказы не принимаются, не должны выгружаться в Яндекс.Маркет.<br /><br />
        ///  available="true" - товарное предложение в наличии. <br />
        ///  Магазин готов сразу договариваться с покупателем о доставке товара
        ///</summary>
        [YmlAttributeForClass]
        [YmlSerializeAs("available")]
        [YmlAttributeOrder(20)]
        public bool? Available { get; set; }

        /// <summary>
        ///   bid (oсновная ставка) — действует на всех местах размещения с возможностью управления ценой клика, если не указана ставка для отдельного места<br />
        ///   Должен иметь целые положительные значения. Например: 21, что соответствует ставке 0,21 у.е.
        /// </summary>
        [YmlAttributeForClass]
        [YmlSerializeAs("bid")]
        [YmlAttributeOrder(30)]
        public uint? BID { get; set; }

        /// <summary>
        ///   cbid (cтавка для карточек) — действует только на карточках моделей.<br />
        ///   Должен иметь целые положительные значения. Например: 21, что соответствует ставке 0,21 у.е.
        /// </summary>
        [YmlAttributeForClass]
        [YmlSerializeAs("cbid")]
        [YmlAttributeOrder(40)]
        public uint? CBID { get; set; }

        /// <summary>
        ///   URL-адрес страницы товара. Максимальная длина URL — 255 символов.
        /// </summary>
        [YmlSerializeAs("url")]
        [YmlElementOrder(0)]
        public string Url { set; get; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("buyurl")]
        [YmlElementOrder(10)]
        public string BuyUrl { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br />
        ///   Цена, по которой данный товар можно приобрести.Цена товарного предложения округляеся и выводится в зависимости от настроек пользователя.
        /// </summary>
        [YmlSerializeAs("price")]
        [YmlFormat("0.##")]
        [YmlElementOrder(20)]
        public double Price { get; set; }

        /// <summary>
        /// </summary>
        [YmlFormat("0.##")]
        [YmlSerializeAs("wprice")]
        [YmlElementOrder(30)]
        public double? Wprice { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br /> 
        ///   Идентификатор валюты товара (RUR, USD, UAH, KZT).<br /> 
        ///   Для корректного отображения цены в национальной валюте, необходимо использовать идентификатор (например, UAH) с соответствующим значением цены.<br />
        /// </summary>
        [YmlSerializeAs("currencyId")]
        [YmlElementOrder(40)]
        public YmlCurrencyId CurrencyId { get; set; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("xCategory")]
        [YmlElementOrder(50)]
        public string Xcategory { get; set; }

        /// <summary>
        ///   Обязательный элемент.<br />
        ///   Идентификатор категории товара (целое число не более 18 знаков).<br /> 
        ///   Товарное предложение может принадлежать только одной категории
        /// </summary>
        [YmlCollection(YmlCollectionSerializationTypes.RecursiveWithNoContainingElement, EachElementName = "categoryId")
        ]
        [YmlElementOrder(60)]
        public List<CategoryID> CategoryIds { get; set; }

        /// <summary>
        ///   Ссылка на картинку соответствующего товарного предложения. <br />
        ///   Недопустимо давать ссылку на "заглушку", т.е. на картинку где написано "картинка отсутствует" или на логотип магазина<br />
        ///   По указанному URL-адресу должна находиться картинка формата jpeg, gif или png размером не меньше 100х100 пикселов. <br />
        ///   Картинки большего размера будут уменьшены автоматически. <br />
        ///   Ссылка на HTML-страницу, содержащую картинку, не допустима.
        /// </summary>
        [YmlSerializeAs("picture")]
        [YmlElementOrder(70)]
        public string Picture { set; get; }

        /// <summary>
        ///   Наличие точки продаж, где товар можно купить без предварительного заказа, указывается значением true у элемента store. <br />
        ///   Если точки продаж нет, используется значение false.
        /// </summary>
        [YmlSerializeAs("store")]
        [YmlElementOrder(80)]
        public bool? Store { get; set; }

        /// <summary>
        ///   Если предусмотрена возможность предварительно заказать данный товар и забрать его в точке продаж, для элемента используется значение true.<br />
        ///   В противном случае — false.
        /// </summary>
        [YmlSerializeAs("pickup")]
        [YmlElementOrder(90)]
        public bool? Pickup { get; set; }

        ///<summary>
        ///  Элемент, обозначающий возможность доставить соответствующий товар. "false" данный товар не может быть доставлен ("самовывоз").<br />
        ///  "true" товар доставляется на условиях, которые указываются в партнерском интерфейсе http://partner.market.yandex.ru на странице "редактирование".
        ///</summary>
        [YmlSerializeAs("delivery")]
        [YmlElementOrder(100)]
        public bool? Delivery { set; get; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("deliveryIncluded")]
        [YmlElementOrder(110)]
        public bool? DeliveryIncluded { get; set; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("local_delivery_cost")]
        [YmlElementOrder(120)]
        public double? LocalDeliveryCost { set; get; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("orderingTime")]
        [YmlElementOrder(130)]
        public OrderingTime OrderingTime { get; set; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("aliases")]
        [YmlElementOrder(400)]
        public string Aliases { get; set; }

        /// <summary>
        /// </summary>
        [YmlCollection(YmlCollectionSerializationTypes.RecursiveWithNoContainingElement, EachElementName = "additional")
        ]
        [YmlElementOrder(410)]
        public List<string> Additionals { get; set; }

        /// <summary>
        ///   Описание товарного предложения
        /// </summary>
        [YmlSerializeAs("description")]
        [YmlElementOrder(420)]
        public string Description { set; get; }

        ///<summary>
        ///  Элемент используется для отражения информации о минимальной сумме заказа, минимальной партии товара или необходимости предоплаты, <br />
        ///  а так же для описания акций магазина (кроме скидок).<br />
        ///  Допустимая длина текста в элементе - 50 символов.
        ///</summary>
        [YmlSerializeAs("sales_notes")]
        [YmlElementOrder(430)]
        public string SalesNotes { get; set; }

        /// <summary>
        /// </summary>
        [YmlSerializeAs("promo")]
        [YmlElementOrder(440)]
        public string Promo { get; set; }

        /// <summary>
        ///   Элемент предназначен для отметки товаров, имеющих официальную гарантию производителя.
        /// </summary>
        [YmlSerializeAs("manufacturer_warranty")]
        [YmlElementOrder(450)]
        public bool? ManufacturerWarranty { set; get; }

        /// <summary>
        ///   Элемент предназначен для указания страны производства товара.
        /// </summary>
        [YmlSerializeAs("country_of_origin")]
        [YmlElementOrder(460)]
        public string CountryOfOrigin { set; get; }

        /// <summary>
        ///   Элемент предназначен для обозначения товара, который можно скачать.
        /// </summary>
        [YmlSerializeAs("downloadable")]
        [YmlElementOrder(470)]
        public bool? Downloadable { get; set; }

        /// <summary>
        ///   Элемент обязателен для обозначения товара, имеющего отношение к удовлетворению сексуальных потребностей,<br /> 
        ///   либо иным образом эксплуатирующего интерес к сексу.
        /// </summary>
        [YmlSerializeAs("adult")]
        [YmlElementOrder(480)]
        public bool? Adult { get; set; }

        /// <summary>
        ///   Штрихкод товара, указанный производителем.
        /// </summary>
        [YmlCollection(YmlCollectionSerializationTypes.RecursiveWithNoContainingElement, EachElementName = "barcode")]
        [YmlElementOrder(490)]
        public List<uint> Barcodes { get; set; }

        ///<summary>
        ///  Характеристики товара.
        ///  Для описания каждого параметра используется отдельный элемент param.
        ///</summary>
        [YmlCollection(YmlCollectionSerializationTypes.RecursiveWithNoContainingElement, EachElementName = "param")]
        [YmlElementOrder(500)]
        public List<Param> Params { get; set; }

        #endregion

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public static IOffer Create()
        {
            return new OfferSimple();
        }

        /// <summary>
        /// </summary>
        /// <param name = "offerType"></param>
        /// <returns></returns>
        public static IOffer Create(YmlOfferType offerType)
        {
            switch(offerType)
            {
                case YmlOfferType.VendorModel:
                    return new OfferVendorModel();
                case YmlOfferType.Book:
                    return new OfferBook();
                case YmlOfferType.AudioBook:
                    return new OfferAudioBook();
                case YmlOfferType.ArtistTitle:
                    return new OfferArtistTitle();
                case YmlOfferType.Tour:
                    return new OfferTour();
                case YmlOfferType.Ticket:
                    return new OfferEventTicket();
                case YmlOfferType.EventTicket:
                    return new OfferEventTicket();
                default:
                    return new OfferSimple();
            }
        }

        internal static IOffer CreateOfferFromXml(XPathNavigator nav)
        {
            DummyOffer dummyOffer = DummyOffer.CreateDummyOfferFromXml(nav);

            IOffer offer;
            string offerType = nav.GetAttribute("type", "");
            switch(offerType)
            {
                case "vendor.model":
                    offer = OfferVendorModel.CreateOfferVendorModelFromXml(nav, dummyOffer);
                    break;
                case "book":
                    offer = OfferBook.CreateOfferBookFromXml(nav, dummyOffer);
                    break;
                case "audiobook":
                    offer = OfferAudioBook.CreateOfferAudioBookFromXml(nav, dummyOffer);
                    break;
                case "artist.title":
                    offer = OfferArtistTitle.CreateOfferArtistTitleFromXml(nav, dummyOffer);
                    break;
                case "tour":
                    offer = OfferTour.CreateOfferTourFromXml(nav, dummyOffer);
                    break;
                case "ticket":
                    offer = OfferEventTicket.CreateOfferEventTicketFromXml(nav, dummyOffer);
                    break;
                case "event-ticket":
                    offer = OfferEventTicket.CreateOfferEventTicketFromXml(nav, dummyOffer);
                    break;
                default:
                    offer = OfferSimple.CreateOfferSimpleFromXml(nav, dummyOffer);
                    break;
            }

            return offer;
        }
    }
}